gusucode.com > VC++ 查看系统安装的驱动器 > VC++ 查看系统安装的驱动器/gusucode/Driveview.cpp
//Download by http://www.NewXing.com //*********************************************************************** // // DriveView.cpp // //*********************************************************************** #include <afxwin.h> #include <afxcview.h> #include <afxcoll.h> #include <afxmt.h> #include "Resource.h" #include "DriveView.h" // Image list indexes #define ILI_HARD_DISK 0 #define ILI_FLOPPY 1 #define ILI_CD_ROM 2 #define ILI_NET_DRIVE 3 #define ILI_RAM_DRIVE 4 #define ILI_CLOSED_FOLDER 5 #define ILI_OPEN_FOLDER 6 IMPLEMENT_DYNCREATE (CDriveView, CTreeView) BEGIN_MESSAGE_MAP (CDriveView, CTreeView) ON_WM_DESTROY () ON_NOTIFY_REFLECT (TVN_ITEMEXPANDING, OnItemExpanding) ON_NOTIFY_REFLECT (TVN_SELCHANGED, OnSelChanged) ON_MESSAGE (WM_USER, OnDriveContentsChanged) END_MESSAGE_MAP () ///////////////////////////////////////////////////////////////////////// // Class constructor CDriveView::CDriveView () : m_event (FALSE, TRUE) { m_nThreadCount = 0; } ///////////////////////////////////////////////////////////////////////// // Overridables BOOL CDriveView::PreCreateWindow (CREATESTRUCT& cs) { if (!CTreeView::PreCreateWindow (cs)) return FALSE; cs.style |= TVS_HASLINES | TVS_LINESATROOT | TVS_HASBUTTONS; return TRUE; } void CDriveView::OnInitialUpdate () { CTreeView::OnInitialUpdate (); m_imglDrives.Create (IDR_DRIVEIMAGES, 16, 1, RGB (255, 0, 255)); GetTreeCtrl ().SetImageList (&m_imglDrives, TVSIL_NORMAL); InitTree (); } void CDriveView::OnSelectionChanged (CString& strPathName) { // // NOTE: Override this function in a derived class to respond // to selection changes differently. The default implementation // calls UpdateAllViews to update other views of the document. // GetDocument ()->UpdateAllViews (this, (LPARAM) (LPCTSTR) strPathName); } ///////////////////////////////////////////////////////////////////////// // Message handlers void CDriveView::OnDestroy () { // Kill all running file change notification threads. if (m_nThreadCount) { m_event.SetEvent (); ::WaitForMultipleObjects (m_nThreadCount, m_hThreads, TRUE, INFINITE); m_nThreadCount = 0; } // Call the base class's OnDestroy handler. CTreeView::OnDestroy (); } void CDriveView::OnItemExpanding (NMHDR* pnmh, LRESULT* pResult) { NM_TREEVIEW* pnmtv = (NM_TREEVIEW*) pnmh; HTREEITEM hItem = pnmtv->itemNew.hItem; CString strPathName = GetPathFromItem (hItem); *pResult = FALSE; // Reset the drive node if the drive is empty or the media changed. if (!IsMediaValid (strPathName)) { HTREEITEM hRoot = GetDriveNode (hItem); GetTreeCtrl ().Expand (hRoot, TVE_COLLAPSE); DeleteChildren (hRoot); AddDummyNode (hRoot); *pResult = TRUE; return; } // Delete the item if strPathName no longer specifies a valid path. if (!IsPathValid (strPathName)) { GetTreeCtrl ().DeleteItem (hItem); *pResult = TRUE; return; } // If the item is expanding, delete the dummy item attached to it // and add folder items. If the item is collapsing instead, delete // its folder items and add a dummy item if appropriate. if (pnmtv->action == TVE_EXPAND) { DeleteChildren (hItem); if (!AddDirectoryNodes (hItem, strPathName)) *pResult = TRUE; } else { DeleteChildren (hItem); if (IsDriveNode (hItem)) AddDummyNode (hItem); else SetButtonState (hItem, strPathName); } } void CDriveView::OnSelChanged (NMHDR* pnmh, LRESULT* pResult) { HTREEITEM hItem = ((NM_TREEVIEW*) pnmh)->itemNew.hItem; CString strPathName = GetPathFromItem (hItem); // Reset the drive node if the drive is empty or the media changed. if (!IsMediaValid (strPathName)) { HTREEITEM hRoot = GetDriveNode (hItem); GetTreeCtrl ().Expand (hRoot, TVE_COLLAPSE); DeleteChildren (hRoot); AddDummyNode (hRoot); return; } // Delete the item if strPathName no longer specifies a valid path. if (!IsPathValid (strPathName)) { GetTreeCtrl ().DeleteItem (hItem); return; } // Update the item's button state if the item is not expanded. if (!(GetTreeCtrl ().GetItemState (hItem, TVIS_EXPANDED) & TVIS_EXPANDED) || !GetTreeCtrl ().ItemHasChildren (hItem)) UpdateButtonState (hItem, strPathName); // Call the view's virtual OnSelectionChanged function. OnSelectionChanged (strPathName); } LONG CDriveView::OnDriveContentsChanged (UINT wParam, LONG lParam) { RefreshDrive ((UINT) wParam); return 0; } ///////////////////////////////////////////////////////////////////////// // Public member functions void CDriveView::RefreshDrive (UINT nDrive) { // Find the HTREEITEM that corresponds to the target drive. CString strDrive = "?:\\"; strDrive.SetAt (0, 0x41 + nDrive); HTREEITEM hItem = FindItem (GetTreeCtrl ().GetNextItem (NULL, TVGN_ROOT), strDrive); if (hItem == NULL) return; // Reset the drive node if the drive is empty or the media changed. if (!IsMediaValid (strDrive)) { GetTreeCtrl ().Expand (hItem, TVE_COLLAPSE); DeleteChildren (hItem); AddDummyNode (hItem); return; } // Save the current drive and directory. char szHome[MAX_PATH]; ::GetCurrentDirectory (sizeof (szHome), szHome); // Change to the root directory of the target drive. if (!::SetCurrentDirectory ((LPCTSTR) strDrive)) return; // Invalid drive specification // Refresh the drive node and all displayed subfolders. if (hItem != NULL) RefreshDirectory (hItem); // Return to the original drive and directory. ::SetCurrentDirectory (szHome); } CString CDriveView::GetPathFromItem (HTREEITEM hItem) { CString strPathName; while (hItem != NULL) { CString string = GetTreeCtrl ().GetItemText (hItem); if ((string.Right (1) != "\\") && !strPathName.IsEmpty ()) string += "\\"; strPathName = string + strPathName; hItem = GetTreeCtrl ().GetParentItem (hItem); } return strPathName; } BOOL CDriveView::ExpandPath (LPCTSTR pszPath, BOOL bSelectItem) { if (::lstrlen (pszPath) < 3) return FALSE; // Begin by finding the corresponding drive node. CString strPathName = pszPath; CString strDrive = strPathName.Left (3); HTREEITEM hItem = FindItem (GetTreeCtrl ().GetNextItem (NULL, TVGN_ROOT), strDrive); if (hItem == NULL) return FALSE; // Invalid drive specification strPathName = strPathName.Right (strPathName.GetLength () - 3); // Now bore down through the directory structure searching for the // item that corresponds to the final directory name in pszPath. while (strPathName.GetLength () > 0) { GetTreeCtrl ().Expand (hItem, TVE_EXPAND); hItem = GetTreeCtrl ().GetChildItem (hItem); if (hItem == NULL) return FALSE; int nIndex = strPathName.Find ('\\'); hItem = FindItem (hItem, nIndex == -1 ? strPathName : strPathName.Left (nIndex)); if (hItem == NULL) return FALSE; // Invalid path name if (nIndex == -1) strPathName.Empty (); else strPathName = strPathName.Right (strPathName.GetLength () - nIndex - 1); } GetTreeCtrl ().Expand (hItem, TVE_EXPAND); if (bSelectItem) GetTreeCtrl ().Select (hItem, TVGN_CARET); return TRUE; } ///////////////////////////////////////////////////////////////////////// // Protected helper functions UINT CDriveView::InitTree () { int nPos = 0; UINT nCount = 0; CString strDrive = "?:\\"; DWORD dwDriveList = ::GetLogicalDrives (); while (dwDriveList) { if (dwDriveList & 1) { strDrive.SetAt (0, 0x41 + nPos); if (AddDriveNode (strDrive)) nCount++; } dwDriveList >>= 1; nPos++; } return nCount; } BOOL CDriveView::AddDriveNode (CString& strDrive) { HTREEITEM hItem; UINT nType = ::GetDriveType ((LPCTSTR) strDrive); UINT nDrive = (UINT) strDrive[0] - 0x41; switch (nType) { case DRIVE_REMOVABLE: hItem = GetTreeCtrl ().InsertItem (strDrive, ILI_FLOPPY, ILI_FLOPPY); AddDummyNode (hItem); m_dwMediaID[nDrive] = GetSerialNumber (strDrive); break; case DRIVE_FIXED: hItem = GetTreeCtrl ().InsertItem (strDrive, ILI_HARD_DISK, ILI_HARD_DISK); SetButtonState (hItem, strDrive); CreateMonitoringThread (strDrive); break; case DRIVE_REMOTE: hItem = GetTreeCtrl ().InsertItem (strDrive, ILI_NET_DRIVE, ILI_NET_DRIVE); SetButtonState (hItem, strDrive); CreateMonitoringThread (strDrive); break; case DRIVE_CDROM: hItem = GetTreeCtrl ().InsertItem (strDrive, ILI_CD_ROM, ILI_CD_ROM); AddDummyNode (hItem); m_dwMediaID[nDrive] = GetSerialNumber (strDrive); break; case DRIVE_RAMDISK: hItem = GetTreeCtrl ().InsertItem (strDrive, ILI_RAM_DRIVE, ILI_RAM_DRIVE); SetButtonState (hItem, strDrive); CreateMonitoringThread (strDrive); break; default: return FALSE; } return TRUE; } UINT CDriveView::AddDirectoryNodes (HTREEITEM hItem, CString& strPathName) { HANDLE hFind; WIN32_FIND_DATA fd; UINT nCount = 0; CString strFileSpec = strPathName; if (strFileSpec.Right (1) != "\\") strFileSpec += "\\"; strFileSpec += "*.*"; if ((hFind = ::FindFirstFile ((LPCTSTR) strFileSpec, &fd)) == INVALID_HANDLE_VALUE) { if (IsDriveNode (hItem)) AddDummyNode (hItem); return 0; } do { if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { CString strFileName = (LPCTSTR) &fd.cFileName; if ((strFileName != ".") && (strFileName != "..")) { HTREEITEM hChild = GetTreeCtrl ().InsertItem ((LPCTSTR) &fd.cFileName, ILI_CLOSED_FOLDER, ILI_OPEN_FOLDER, hItem, TVI_SORT); CString strNewPathName = strPathName; if (strNewPathName.Right (1) != "\\") strNewPathName += "\\"; strNewPathName += (LPCTSTR) &fd.cFileName; SetButtonState (hChild, strNewPathName); nCount++; } } } while (::FindNextFile (hFind, &fd)); ::FindClose (hFind); return nCount; } void CDriveView::SetButtonState (HTREEITEM hItem, CString& strPathName) { if (HasSubdirectory (strPathName)) AddDummyNode (hItem); } void CDriveView::UpdateButtonState (HTREEITEM hItem, CString& strPathName) { if (HasSubdirectory (strPathName)) { if (!GetTreeCtrl ().ItemHasChildren (hItem)) { AddDummyNode (hItem); Invalidate (); } } else { if (GetTreeCtrl ().ItemHasChildren (hItem)) DeleteChildren (hItem); } } BOOL CDriveView::HasSubdirectory (CString& strPathName) { HANDLE hFind; WIN32_FIND_DATA fd; BOOL bResult = FALSE; CString strFileSpec = strPathName; if (strFileSpec.Right (1) != "\\") strFileSpec += "\\"; strFileSpec += "*.*"; if ((hFind = ::FindFirstFile ((LPCTSTR) strFileSpec, &fd)) != INVALID_HANDLE_VALUE) { do { if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { CString strFileName = (LPCTSTR) &fd.cFileName; if ((strFileName != ".") && (strFileName != "..")) bResult = TRUE; } } while (::FindNextFile (hFind, &fd) && !bResult); ::FindClose (hFind); } return bResult; } BOOL CDriveView::IsDriveNode (HTREEITEM hItem) { return (GetTreeCtrl ().GetParentItem (hItem) == NULL) ? TRUE : FALSE; } void CDriveView::AddDummyNode (HTREEITEM hItem) { GetTreeCtrl ().InsertItem ("", 0, 0, hItem); } HTREEITEM CDriveView::FindItem (HTREEITEM hItem, CString& strTarget) { while (hItem != NULL) { if (GetTreeCtrl ().GetItemText (hItem) == strTarget) break; hItem = GetTreeCtrl ().GetNextSiblingItem (hItem); } return hItem; } UINT CDriveView::DeleteChildren (HTREEITEM hItem) { UINT nCount = 0; HTREEITEM hChild = GetTreeCtrl ().GetChildItem (hItem); while (hChild != NULL) { HTREEITEM hNextItem = GetTreeCtrl ().GetNextSiblingItem (hChild); GetTreeCtrl ().DeleteItem (hChild); hChild = hNextItem; nCount++; } return nCount; } HTREEITEM CDriveView::GetDriveNode (HTREEITEM hItem) { HTREEITEM hParent; do { hParent = GetTreeCtrl ().GetParentItem (hItem); if (hParent != NULL) hItem = hParent; } while (hParent != NULL); return hItem; } DWORD CDriveView::GetSerialNumber (CString& strDrive) { DWORD dwSerialNumber; if (!::GetVolumeInformation ((LPCTSTR) strDrive, NULL, 0, &dwSerialNumber, NULL, NULL, NULL, 0)) dwSerialNumber = 0xFFFFFFFF; return dwSerialNumber; } BOOL CDriveView::IsMediaValid (CString& strPathName) { // Return TRUE if the drive doesn't support removable media. UINT nDriveType = GetDriveType ((LPCTSTR) strPathName); if ((nDriveType != DRIVE_REMOVABLE) && (nDriveType != DRIVE_CDROM)) return TRUE; // Return FALSE if the drive is empty (::GetVolumeInformation fails). DWORD dwSerialNumber; CString strDrive = strPathName.Left (3); UINT nDrive = (UINT) strDrive[0] - 0x41; if (!::GetVolumeInformation ((LPCTSTR) strDrive, NULL, 0, &dwSerialNumber, NULL, NULL, NULL, 0)) { m_dwMediaID[nDrive] = 0xFFFFFFFF; return FALSE; } // Also return FALSE if the disk's serial number has changed. if ((m_dwMediaID[nDrive] != dwSerialNumber) && (m_dwMediaID[nDrive] != 0xFFFFFFFF)) { m_dwMediaID[nDrive] = dwSerialNumber; return FALSE; } // Update our record of the serial number and return TRUE. m_dwMediaID[nDrive] = dwSerialNumber; return TRUE; } BOOL CDriveView::IsPathValid (CString& strPathName) { if (strPathName.GetLength () == 3) return TRUE; HANDLE hFind; WIN32_FIND_DATA fd; BOOL bResult = FALSE; if ((hFind = ::FindFirstFile ((LPCTSTR) strPathName, &fd)) != INVALID_HANDLE_VALUE) { if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) bResult = TRUE; ::CloseHandle (hFind); } return bResult; } void CDriveView::RefreshDirectory (HTREEITEM hItem) { // If the item is not expanded, update its button state and return. if (!(GetTreeCtrl ().GetItemState (hItem, TVIS_EXPANDED) & TVIS_EXPANDED) || !GetTreeCtrl ().ItemHasChildren (hItem)) { if (!IsDriveNode (hItem)) { CString strPathName = GetPathFromItem (hItem); UpdateButtonState (hItem, strPathName); GetTreeCtrl ().Expand (hItem, TVE_COLLAPSE); } return; } // Delete items corresponding to subdirectories that no longer exist // and build a CStringList containing the names of all the items that // are children of hItem. CStringList list; WIN32_FIND_DATA fd; HANDLE hFind; HTREEITEM hChild = GetTreeCtrl ().GetChildItem (hItem); while (hChild != NULL) { HTREEITEM hNextItem = GetTreeCtrl ().GetNextSiblingItem (hChild); CString strDirName = GetTreeCtrl ().GetItemText (hChild); if ((hFind = ::FindFirstFile ((LPCTSTR) strDirName, &fd)) != INVALID_HANDLE_VALUE) { if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) list.AddTail (strDirName); else GetTreeCtrl ().DeleteItem (hChild); ::FindClose (hFind); } else GetTreeCtrl ().DeleteItem (hChild); hChild = hNextItem; } // Add items for newly created subdirectories. if ((hFind = ::FindFirstFile ("*.*", &fd)) != INVALID_HANDLE_VALUE) { do { if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { CString strFileName = (LPCTSTR) &fd.cFileName; if ((strFileName != ".") && (strFileName != "..")) { if (list.Find (strFileName) == NULL) { hChild = GetTreeCtrl ().InsertItem (strFileName, ILI_CLOSED_FOLDER, ILI_OPEN_FOLDER, hItem, TVI_SORT); CString strPathName = GetPathFromItem (hChild); SetButtonState (hChild, strPathName); list.AddTail (strFileName); } } } } while (::FindNextFile (hFind, &fd)); ::FindClose (hFind); } // Remove all items from the CStringList. list.RemoveAll (); // Now repeat this procedure for hItem's children. hChild = GetTreeCtrl ().GetChildItem (hItem); while (hChild != NULL) { CString string = GetTreeCtrl ().GetItemText (hChild); ::SetCurrentDirectory ((LPCTSTR) string); RefreshDirectory (hChild); // Recursion! ::SetCurrentDirectory (".."); hChild = GetTreeCtrl ().GetNextSiblingItem (hChild); } } void CDriveView::CreateMonitoringThread (CString& strDrive) { PTHREADINFO pThreadInfo = new THREADINFO; // Thread will delete pThreadInfo->nDrive = (UINT) strDrive[0] - 0x41; pThreadInfo->hEvent = m_event.m_hObject; pThreadInfo->hWnd = m_hWnd; CWinThread* pThread = AfxBeginThread (ThreadFunc, pThreadInfo, THREAD_PRIORITY_IDLE); m_hThreads[m_nThreadCount++] = pThread->m_hThread; } ///////////////////////////////////////////////////////////////////////// // Thread function for detecting file system changes UINT CDriveView::ThreadFunc (LPVOID pParam) { PTHREADINFO pThreadInfo = (PTHREADINFO) pParam; UINT nDrive = pThreadInfo->nDrive; HANDLE hEvent = pThreadInfo->hEvent; HWND hWnd = pThreadInfo->hWnd; delete pThreadInfo; CString strDrive = "?:\\"; strDrive.SetAt (0, 0x41 + nDrive); // Get a handle to a file change notification object. HANDLE hChange = ::FindFirstChangeNotification ((LPCTSTR) strDrive, TRUE, FILE_NOTIFY_CHANGE_DIR_NAME); // Return now if ::FindFirstChangeNotification failed. if (hChange == INVALID_HANDLE_VALUE) return 1; HANDLE aHandles[2]; aHandles[0] = hChange; aHandles[1] = hEvent; BOOL bContinue = TRUE; // Sleep until a file change notification wakes this thread or // m_event becomes set indicating it's time for the thread to end. while (bContinue) { if (::WaitForMultipleObjects (2, aHandles, FALSE, INFINITE) - WAIT_OBJECT_0 == 0) { // Respond to a change notification. ::PostMessage (hWnd, WM_USER, (WPARAM) nDrive, 0); ::FindNextChangeNotification (hChange); } else // Kill this thread (m_event became signaled). bContinue = FALSE; } // Close the file change notification handle and return. ::FindCloseChangeNotification (hChange); return 0; }